package com.fa4j.code.mate.repository.impl;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.fa4j.code.mate.convert.CodeMateConverter;
import com.fa4j.code.mate.model.*;
import com.fa4j.code.mate.repository.CodeMateRepository;
import com.fa4j.code.mate.repository.mapper.CodeMateMapper;
import com.fa4j.common.base.model.Page;
import com.fa4j.common.data.repository.impl.BaseRepositoryImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 代码元数据 持久化接口
 *
 * @since 2025-09-14 11:40:42
 */
@Repository
@RequiredArgsConstructor
public class CodeMateRepositoryImpl extends BaseRepositoryImpl<CodeMateMapper, CodeMate> implements CodeMateRepository {

    private final CodeMateConverter codeMateConverter;

    @Override
    public Optional<CodeMateProject> findProjectByProjectName(String projectName) {
        List<CodeMate> mateList = super.lambdaQuery().eq(CodeMate::getProjectName, projectName).list();
        Map<CodeMateType, List<CodeMate>> typeListMap = mateList.stream().collect(Collectors.groupingBy(CodeMate::getType));
        if (!typeListMap.containsKey(CodeMateType.PROJECT)) {
            return Optional.empty();
        }
        Optional<CodeMateProject> projectOpt = typeListMap.get(CodeMateType.PROJECT).stream().map(codeMateConverter::toProject).findFirst();
        if (projectOpt.isPresent()) {
            CodeMateProject project = projectOpt.get();
            setSubList(project, typeListMap.get(CodeMateType.MODULE), codeMateConverter::toModule, project::setModuleList);
            for (CodeMateModule module : project.getModuleList()) {
                setSubList(module, typeListMap.get(CodeMateType.MODEL), codeMateConverter::toModel, module::setModelList);
                for (CodeMateModel model : module.getModelList()) {
                    setSubList(model, typeListMap.get(CodeMateType.FIELD), codeMateConverter::toField, model::setFieldList);
                    setSubList(model, typeListMap.get(CodeMateType.METHOD), codeMateConverter::toMethod, model::setMethodList);
                    for (CodeMateMethod method : model.getMethodList()) {
                        setSubList(method, typeListMap.get(CodeMateType.FIELD), codeMateConverter::toField, method::setParamFields);
                    }
                }
            }
        }
        return projectOpt;
    }

    private <P extends AbstractCodeMate, T extends AbstractCodeMate> void setSubList(
            P parent, List<CodeMate> codeMates, Function<CodeMate, T> mapper, Consumer<List<T>> consumer
    ) {
        List<T> mateList = new ArrayList<>();
        if (codeMates != null && !codeMates.isEmpty()) {
            mateList = codeMates.stream()
                    .filter(m -> m.getParentId().equals(parent.getId()))
                    .map(mapper)
                    .sorted(Comparator.comparing(T::getPriority))
                    .toList();
        }
        consumer.accept(mateList);
    }

    @Override
    public Page<CodeMateProject> queryProjectPage(CodeMateProjectQuery query) {
        CodeMateQuery mateQuery = codeMateConverter.toMateQuery(query);
        Page<CodeMate> page = super.queryPage(mateQuery);
        List<CodeMateProject> records = page.getRecords().stream().map(codeMateConverter::toProject).toList();
        return Page.<CodeMateProject>builder()
                .current(page.getCurrent())
                .size(page.getSize())
                .total(page.getTotal())
                .records(records)
                .orderBy(query.getOrderBy())
                .orderAsc(query.getOrderAsc())
                .build();
    }

    @Override
    public Optional<CodeMate> findByTypeAndProjectName(CodeMateType type, String projectName) {
        return super.lambdaQuery()
                .eq(CodeMate::getType, type)
                .eq(CodeMate::getProjectName, projectName)
                .oneOpt();
    }

    @Override
    public Optional<CodeMate> findByTypesAndModuleName(List<CodeMateType> types, String moduleName) {
        return super.lambdaQuery()
                .in(CodeMate::getType, types)
                .eq(CodeMate::getModuleName, moduleName)
                .oneOpt();
    }

    @Override
    public Optional<CodeMate> findByTypesAndModuleCode(List<CodeMateType> types, Integer moduleCode) {
        return super.lambdaQuery()
                .in(CodeMate::getType, types)
                .eq(CodeMate::getModuleCode, moduleCode)
                .oneOpt();
    }

    @Override
    public Optional<CodeMate> findByProjectNameAndTypeAndName(String projectName, CodeMateType type, String name) {
        return super.lambdaQuery()
                .eq(CodeMate::getProjectName, projectName)
                .eq(CodeMate::getType, type)
                .eq(CodeMate::getName, name)
                .oneOpt();
    }

    @Override
    public Optional<CodeMate> findByParentIdAndName(Integer parentId, String name) {
        return super.lambdaQuery()
                .in(CodeMate::getParentId, parentId)
                .eq(CodeMate::getName, name)
                .oneOpt();
    }


    @Override
    public List<CodeMate> queryByParentId(Integer parentId) {
        return super.lambdaQuery()
                .eq(CodeMate::getParentId, parentId)
                .list();
    }

    @Override
    public void modifyByModuleCode(Integer targetModuleCode, Integer sourceModuleCode, Long operatorId) {
        CodeMate codeMate = new CodeMate();
        codeMate.setModuleCode(targetModuleCode);
        codeMate.setOperatorId(operatorId);
        super.update(codeMate,
                new UpdateWrapper<CodeMate>().lambda()
                        .eq(CodeMate::getModuleCode, sourceModuleCode)
        );
    }

    @Override
    public void modifyByModuleName(String targetModuleName, String sourceModuleName, Long operatorId) {
        CodeMate codeMate = new CodeMate();
        codeMate.setModuleName(targetModuleName);
        codeMate.setOperatorId(operatorId);
        super.update(codeMate,
                new UpdateWrapper<CodeMate>().lambda()
                        .eq(CodeMate::getModuleName, sourceModuleName)
        );
    }

    @Override
    public void modifyByProjectName(String targetProjectName, String sourceProjectName, Long operatorId) {
        CodeMate codeMate = new CodeMate();
        codeMate.setProjectName(targetProjectName);
        codeMate.setOperatorId(operatorId);
        super.update(codeMate,
                new UpdateWrapper<CodeMate>().lambda()
                        .eq(CodeMate::getProjectName, sourceProjectName)
        );
    }
}
